[MFC]程序示例:三子棋游戏

1. 游戏功能简介:

    1) 3×3棋盘,9宫格,每格可放一个棋子;

    2) 鼠标左键落X右键落O,保证X和O轮流出现防止作弊,并且设定X为先手;

    3) 棋盘是井字形的框,鼠标双击井字框将重置棋局;

    4) 水平、垂直或对角线3连线即赢得棋局;


2. TicTac.h:

// TicTac.h

class CMyApp: public CWinApp
{
public:
	virtual BOOL InitInstance();
};

#define EX		1 // 先手X
#define OH		2 // 后手O

class CMyWindow: public CWnd // 注意!从CWnd继承而不是CFrameWnd继承
{
protected:
	static const CRect m_rcSquares[9]; // 格子的坐标(只需要左上角和右下角坐标即可)
	int m_nGameGrid[9]; // 格子中的内容,是哪个玩家的棋子
	int m_nNextChar; // 下一个落子者,初始化成EX

protected:
	int GetRectID(CPoint point); // 根据点的位置返回点落在哪个方格中
	void DrawBoard(CDC* pDC); // 画整个棋局板,包括方块内的棋子
	void DrawX(CDC* pDC, int nPos); // 在nPos号方格中画X
	void DrawO(CDC* pDC, int nPos); // 在nPos号方格中画O
	void ResetGame(); // 重置棋局
	void CheckForGameOver(); // 检查棋局是否结束(哪方赢或者平局)
	int IsWinner(); // 返回谁是胜者,还没决出就返回0,否则返回EX或者OH
	BOOL IsDraw(); // 检查是否平局

public:
	CMyWindow();

protected:
	virtual void PostNcDestroy(); // 从CWnd继承的窗口必须覆盖该函数,用以销毁窗口非客户区

	afx_msg void OnPaint();
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point); // EX落子
	afx_msg void OnRButtonDown(UINT nFlags, CPoint point); // OH落子
	afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); // 双击井字线条重新开一局

	DECLARE_MESSAGE_MAP()
};


3. TicTac.cpp的开头部分(包括静态成员变量的定义):

// TicTac.cpp

#include <afxwin.h>

#include "TicTac.h"

CMyApp myApp;

BOOL CMyApp::InitInstance()
{
	m_pMainWnd = new CMyWindow;
	m_pMainWnd->ShowWindow(m_nCmdShow);
	m_pMainWnd->UpdateWindow();

	return TRUE;
}

BEGIN_MESSAGE_MAP(CMyWindow, CWnd)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_RBUTTONDOWN()
	ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()

const CRect CMyWindow::m_rcSquares[9] = { // 9个格子在客户区中的坐标
	CRect (	16,  16, 112, 112), // 格子按照从上到下从左到右编号0-8
	CRect (128,  16, 224, 112),
	CRect (240,  16, 336, 112),
	CRect ( 16, 128, 112, 224),
	CRect (128, 128, 224, 224),
	CRect (240, 128, 336, 224),
	CRect ( 16, 240, 112, 336),
	CRect (128, 240, 224, 336),
    CRect (240, 240, 336, 336)
};

4. GetRectID:

     1) 用以判断鼠标击键是否位于9个方格内;

     2) 使用到CRect的成员函数来判断一个点是否位于矩形区域内:BOOL CRect::PtInRect(POINT point) const;

int CMyWindow::GetRectID(CPoint point)
{// 判断点point落在几号格子中
	for (int i = 0; i < 9; i++) {
		if (m_rcSquares[i].PtInRect(point))
			return i;
	}
	return -1; // 落在所有格子的外面
}

5. DrawX:

     1) 使用到了CRect的成员函数用来将缩小矩形:void CRect::DeflateRect(int x, int y);,意义是使矩形左右两边分别向中心靠拢x个单位,上下两边分别向中心靠拢y个单位;

     2) 函数将在一个缩小过的方格内画叉,这样不会使叉和井字棋盘边框重叠,更加美观;

void CMyWindow::DrawX(CDC* pDC, int nPos)
{// 在nPos号格子中画X
	CPen pen(PS_SOLID, 16, RGB(255, 0, 0)); // X是红色16宽的实线
	CPen* pOldPen = pDC->SelectObject(&pen);

	CRect rect = m_rcSquares[nPos]; // 将nPos号格子的坐标下载到本地处理
	rect.DeflateRect(16, 16); // 四周向中心收缩16像素以避免X和格角重合,更加美观

	// 画X
	pDC->MoveTo(rect.left, rect.top);
	pDC->LineTo(rect.right, rect.bottom);
	pDC->MoveTo(rect.left, rect.bottom);
	pDC->LineTo(rect.right, rect.top);

	pDC->SelectObject(pOldPen); // !记得还原默认的画笔
}

6. DrawY:

    1) 在方格中画圆,要求使圆中的填充色为透明,所以需要使用透明画刷;

    2) 之前讲过通过CBrush的构造函数、CreateSolidBrush、CreateHatchBrush只能创建单色或者阴影线型的画刷,但是要指定透明画刷只能通过创建逻辑画刷将参数lbStyle设为BS_NULL或BS_HOLLOW,但是这样做实在非常繁琐,但是还有更加简便指定画刷样式的方式,就是使用SelectStockObject将一个库存中定义过的指定样式的画刷直接选入设备环境,连创建画刷的步骤都不需要:

virtual CGdiObject* CDC::SelectStockObject(int nIndex);

!其中nIndex是系统预定好的GDI对象的ID号,比如NULL_BRUSH,就是我们需要用的透明画刷,BLACK_BRUSH即黑色画刷,BLACK_PEN即黑色画笔,如果需要用到这些简单的预订好的GDI对象则可以直接使用该函数将相关对象选入设备环境;

!该函数可选中的GDI对象有画笔、画刷、字体这三

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值